package hash

//5%10=5   5，15，25
//哈希碰撞  15，5%10=5
//哈希容量
//8个人，一周内离开了北京，必定有一天，是两个人以上都离开

import "crypto/sha1"   //速度一般，安全性一般  2^128
import "crypto/sha256" //速度慢安全性高   2^256   1
import "crypto/md5"    //速度一般，安全性一般  2^128
import "crypto/sha512" //速度慢，安全性极高  2^512  1

import "hash/crc32"   //速度快，安全性低  2^32
import "hash/crc64"   //速度稍微快，安全性低  2^64
import "hash/adler32" //速度快，安全性低  2^32 4亿
import "hash/fnv"     //自由定制32，64，128，安全性低

import "encoding/hex"
import "fmt"
import "io"
import "encoding"
import "os"
import "path/filepath"

type AllHash struct {
	Alog []string
}

func (allhash *AllHash) SetAlog(alog []string) {
	allhash.Alog = alog
}
func (allhash *AllHash) SetN(n int) {

}
func (allhash *AllHash) GetBytesHash(data []byte) string {
	var laststr string
	laststr = string(data)
	for i := 0; i < len(allhash.Alog); i++ {

		switch allhash.Alog[i] {
		case "md5":
			myhash := md5.New()
			myhash.Write([]byte(laststr))
			bs := myhash.Sum(nil)
			laststr = hex.EncodeToString(bs)
		case "sha1":
			myhash := sha1.New()
			myhash.Write([]byte(laststr))
			bs := myhash.Sum(nil)
			laststr = hex.EncodeToString(bs)
		case "sha256":
			myhash := sha256.New()
			myhash.Write([]byte(laststr))
			bs := myhash.Sum(nil)
			laststr = hex.EncodeToString(bs)
		case "sha512":
			myhash := sha512.New()
			myhash.Write([]byte(laststr))
			bs := myhash.Sum(nil)
			laststr = hex.EncodeToString(bs)
		case "crc32":
			mycrc := crc32.NewIEEE()
			io.WriteString(mycrc, laststr)
			laststr = fmt.Sprintf("%x", mycrc.Sum32())
		case "crc64":
			const ISO = 0xD800000000000000
			tabISO := MakeTable(ISO)
			c := crc64.New(tabISO)
			io.WriteString(c, laststr)
			s := c.Sum64()
			laststr = fmt.Sprintf("%x", s)
		case "adler32":
			c := adler32.New()
			io.WriteString(c, laststr)
			state, err := c.(encoding.BinaryMarshaler).MarshalBinary()
			if err != nil {
				fmt.Println(err)
			}
			laststr = hex.EncodeToString(state)

		case "fnv32":
			h := fnv.New32()
			h.Write([]byte(laststr))
			bs := h.Sum(nil)
			laststr = hex.EncodeToString(bs)
		case "fnv64":
			h := fnv.New64()
			h.Write([]byte(laststr))
			bs := h.Sum(nil)
			laststr = hex.EncodeToString(bs)
		case "fnv128":
			h := fnv.New128()
			h.Write([]byte(laststr))
			bs := h.Sum(nil)
			laststr = hex.EncodeToString(bs)
		default:

		}

	}
	return laststr

}
func (allhash *AllHash) GetStringHash(datastr string) string {

	var laststr string
	laststr = datastr
	for i := 0; i < len(allhash.Alog); i++ {

		switch allhash.Alog[i] {
		case "md5":
			myhash := md5.New()
			myhash.Write([]byte(laststr))
			bs := myhash.Sum(nil)
			laststr = hex.EncodeToString(bs)
		case "sha1":
			myhash := sha1.New()
			myhash.Write([]byte(laststr))
			bs := myhash.Sum(nil)
			laststr = hex.EncodeToString(bs)
		case "sha256":
			myhash := sha256.New()
			myhash.Write([]byte(laststr))
			bs := myhash.Sum(nil)
			laststr = hex.EncodeToString(bs)
		case "sha512":
			myhash := sha512.New()
			myhash.Write([]byte(laststr))
			bs := myhash.Sum(nil)
			laststr = hex.EncodeToString(bs)
		case "crc32":
			mycrc := crc32.NewIEEE()
			io.WriteString(mycrc, datastr)
			laststr = fmt.Sprintf("%x", mycrc.Sum32())
		case "crc64":
			const ISO = 0xD800000000000000
			tabISO := MakeTable(ISO)
			c := crc64.New(tabISO)
			io.WriteString(c, datastr)
			s := c.Sum64()
			laststr = fmt.Sprintf("%x", s)
		case "adler32":
			c := adler32.New()
			io.WriteString(c, datastr)
			state, err := c.(encoding.BinaryMarshaler).MarshalBinary()
			if err != nil {
				fmt.Println(err)
			}
			laststr = hex.EncodeToString(state)

		case "fnv32":
			h := fnv.New32()
			h.Write([]byte(datastr))
			bs := h.Sum(nil)
			laststr = hex.EncodeToString(bs)
		case "fnv64":
			h := fnv.New64()
			h.Write([]byte(datastr))
			bs := h.Sum(nil)
			laststr = hex.EncodeToString(bs)
		case "fnv128":
			h := fnv.New128()
			h.Write([]byte(datastr))
			bs := h.Sum(nil)
			laststr = hex.EncodeToString(bs)
		default:

		}

	}
	return laststr
}
func (allhash *AllHash) GetFileHash(filepath string) string {

	var hashValue string
	file, err := os.Open(filepath)
	if err != nil {
		return ""
	}
	defer file.Close()
	hash := sha512.New()
	if _, err := io.Copy(hash, file); err != nil {
		return ""
	}
	hashbyte := hash.Sum(nil)
	hashValue = hex.EncodeToString(hashbyte) //得到最终哈希字符串

	hashValue = allhash.GetStringHash(hashValue)
	return hashValue
}
func (allhash *AllHash) GetFiledirHashName(filedirpath string) string {
	filedata := ""
	err := filepath.Walk(filedirpath, func(path string, info os.FileInfo, err error) error {
		if info == nil {
			return err
		}
		if info.IsDir() {
			//fmt.Println("文件夹",path)
			filedata += path
			return nil
		}
		//fmt.Println("文件",path)
		filedata += path
		return nil
	})
	if err != nil {
		return ""
	} else {
		myhash := sha256.New()
		myhash.Write([]byte(filedata))
		bs := myhash.Sum(nil)
		hashValue := hex.EncodeToString(bs)
		hashValue = allhash.GetStringHash(hashValue)
		return hashValue
	}

	return ""
}
func (allhash *AllHash) GetFiledirHashContent(filedirpath string) string {
	filedata := ""
	err := filepath.Walk(filedirpath, func(path string, info os.FileInfo, err error) error {
		if info == nil {
			return err
		}
		if info.IsDir() {
			return nil
		}
		//fmt.Println("文件",path)
		filedata += (allhash.GetFileHash(path))
		return nil
	})
	if err != nil {
		return ""
	} else {
		myhash := sha256.New()
		myhash.Write([]byte(filedata))
		bs := myhash.Sum(nil)
		hashValue := hex.EncodeToString(bs)
		hashValue = allhash.GetStringHash(hashValue)
		return hashValue
	}

}

func MakeTable(poly uint64) *crc64.Table {
	switch poly {
	case 0xD800000000000000:
		var slicing8TableISO = makeSlicingBy8Table(makeTable(0xD800000000000000))
		return &slicing8TableISO[0]
	default:
		return makeTable(poly)
	}
}

func makeTable(poly uint64) *crc64.Table {
	t := new(crc64.Table)
	for i := 0; i < 256; i++ {
		crc := uint64(i)
		for j := 0; j < 8; j++ {
			if crc&1 == 1 {
				crc = (crc >> 1) ^ poly
			} else {
				crc >>= 1
			}
		}
		t[i] = crc
	}
	return t
}
func makeSlicingBy8Table(t *crc64.Table) *[8]crc64.Table {
	var helperTable [8]crc64.Table
	helperTable[0] = *t
	for i := 0; i < 256; i++ {
		crc := t[i]
		for j := 1; j < 8; j++ {
			crc = t[crc&0xff] ^ (crc >> 8)
			helperTable[j][i] = crc
		}
	}
	return &helperTable
}
